﻿//-------------------------------------------------------------------
//	Copyright (c) 2012  Zhirnov Andrey
//	This file is part of the "OGL-Tools" project.
//	See copyright notice in "Logger.h".
//-------------------------------------------------------------------

#include "gl_logger.h"


namespace gl
{
	using namespace UX_STL;
	using namespace UXTypes;
	using namespace UXTypesExt;
	using namespace UXLogger;
	using namespace UXFileSystem;

#	define CONST	const


	static string		s_sGlobalString;

	
/*
=================================================
	возвращает указатель на синглтон
=================================================
*/
	GLSrcFunctions * Instance()
	{
		return SingletonMultiThread::Instance< GLSrcFunctions >();
	}
	
/*
=================================================
	запись значения в строку
=================================================
*/
	template <typename T>
	inline string & str_add(UXTypes::string &s, const T& value)
	{
		s << value;
		return s;
	}
	
	inline string & str_add(UXTypes::string &s, const GLchar * str)
	{
		s << '"' << (const char *)str << '"';
		return s;
	}

	inline string & str_add(UXTypes::string &s, const GLubyte * str)
	{
		s << '"' << (const char *)str << '"';
		return s;
	}
	
	template <typename T>
	inline string & str_add(UXTypes::string &s, const T * ptr)
	{
		s << "*0x" << string().FormatI( (usize)ptr, 16 );
		return s;
	}
	
	template <typename T>
	inline string & str_add(UXTypes::string &s, const T ** ptr)
	{
		s << "**0x" << string().FormatI( (usize)ptr, 16 );
		return s;
	}
	
	template <typename T>
	inline string & str_add(UXTypes::string &s, T * ptr)
	{
		s << "*0x" << string().FormatI( (usize)ptr, 16 );
		return s;
	}
	
	template <typename T>
	inline string & str_add(UXTypes::string &s, T ** ptr)
	{
		s << "**0x" << string().FormatI( (usize)ptr, 16 );
		return s;
	}

	
/*
=================================================
	запись параметров функции в строку
=================================================
*/
	inline void ToLog()
	{}

	template <typename T0>
	inline void ToLog(const T0 &a0)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );
	}
	
	template <typename T0, typename T1>
	inline void ToLog(const T0 &a0, const T1 &a1)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );
	}
	
	template <typename T0, typename T1, typename T2>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9,
			  typename T10>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9,
					  const T10 &a10)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );		str << ", ";
		str_add( str, a10 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9,
			  typename T10, typename T11>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9,
					  const T10 &a10, const T11 &a11)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );		str << ", ";
		str_add( str, a10 );	str << ", ";
		str_add( str, a11 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9,
			  typename T10, typename T11, typename T12>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9,
					  const T10 &a10, const T11 &a11, const T12 &a12)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );		str << ", ";
		str_add( str, a10 );	str << ", ";
		str_add( str, a11 );	str << ", ";
		str_add( str, a12 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9,
			  typename T10, typename T11, typename T12, typename T13>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9,
					  const T10 &a10, const T11 &a11, const T12 &a12, const T13 &a13)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );		str << ", ";
		str_add( str, a10 );	str << ", ";
		str_add( str, a11 );	str << ", ";
		str_add( str, a12 );	str << ", ";
		str_add( str, a13 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9,
			  typename T10, typename T11, typename T12, typename T13, typename T14>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9,
					  const T10 &a10, const T11 &a11, const T12 &a12, const T13 &a13, const T14 &a14)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );		str << ", ";
		str_add( str, a10 );	str << ", ";
		str_add( str, a11 );	str << ", ";
		str_add( str, a12 );	str << ", ";
		str_add( str, a13 );	str << ", ";
		str_add( str, a14 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9,
			  typename T10, typename T11, typename T12, typename T13, typename T14,
			  typename T15>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9,
					  const T10 &a10, const T11 &a11, const T12 &a12, const T13 &a13, const T14 &a14,
					  const T15 &a15)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );		str << ", ";
		str_add( str, a10 );	str << ", ";
		str_add( str, a11 );	str << ", ";
		str_add( str, a12 );	str << ", ";
		str_add( str, a13 );	str << ", ";
		str_add( str, a14 );	str << ", ";
		str_add( str, a15 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9,
			  typename T10, typename T11, typename T12, typename T13, typename T14,
			  typename T15, typename T16>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9,
					  const T10 &a10, const T11 &a11, const T12 &a12, const T13 &a13, const T14 &a14,
					  const T15 &a15, const T16 &a16)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );		str << ", ";
		str_add( str, a10 );	str << ", ";
		str_add( str, a11 );	str << ", ";
		str_add( str, a12 );	str << ", ";
		str_add( str, a13 );	str << ", ";
		str_add( str, a14 );	str << ", ";
		str_add( str, a15 );	str << ", ";
		str_add( str, a16 );
	}
	
	template <typename T0, typename T1, typename T2, typename T3, typename T4,
			  typename T5, typename T6, typename T7, typename T8, typename T9,
			  typename T10, typename T11, typename T12, typename T13, typename T14,
			  typename T15, typename T16, typename T17>
	inline void ToLog(const T0 &a0, const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4,
					  const T5 &a5, const T6 &a6, const T7 &a7, const T8 &a8, const T9 &a9,
					  const T10 &a10, const T11 &a11, const T12 &a12, const T13 &a13, const T14 &a14,
					  const T15 &a15, const T16 &a16, const T17 &a17)
	{
		string	& str = s_sGlobalString;
		str_add( str, a0 );		str << ", ";
		str_add( str, a1 );		str << ", ";
		str_add( str, a2 );		str << ", ";
		str_add( str, a3 );		str << ", ";
		str_add( str, a4 );		str << ", ";
		str_add( str, a5 );		str << ", ";
		str_add( str, a6 );		str << ", ";
		str_add( str, a7 );		str << ", ";
		str_add( str, a8 );		str << ", ";
		str_add( str, a9 );		str << ", ";
		str_add( str, a10 );	str << ", ";
		str_add( str, a11 );	str << ", ";
		str_add( str, a12 );	str << ", ";
		str_add( str, a13 );	str << ", ";
		str_add( str, a14 );	str << ", ";
		str_add( str, a15 );	str << ", ";
		str_add( str, a16 );	str << ", ";
		str_add( str, a17 );
	}
	
/*
=================================================
	проверка на возникновение ошибок,
	если есть, то записывается в строку
=================================================
*/
	bool CheckGLError()
	{
		GLenum	err = GL_CALL( glGetError()() );

		if ( err == gl::GL_NO_ERROR )
			return true;

		s_sGlobalString << "\nError generated: ";

		switch ( err )
		{
			case GL_INVALID_ENUM:
				s_sGlobalString << "GL_INVALID_ENUM";
				break;

			case GL_INVALID_VALUE:
				s_sGlobalString << "GL_INVALID_VALUE";
				break;

			case GL_INVALID_OPERATION:
				s_sGlobalString << "GL_INVALID_OPERATION";
				break;

			case GL_INVALID_FRAMEBUFFER_OPERATION:
				s_sGlobalString << "GL_INVALID_FRAMEBUFFER_OPERATION";
				break;

			case GL_OUT_OF_MEMORY:
				s_sGlobalString << "GL_OUT_OF_MEMORY";
				break;

			default:
				s_sGlobalString << "code 0x" << UXTypes::string().FormatI( err, 16 );
				break;
		}
		return false;
	}




	//
	// GL Return Type
	//

	template <typename T>
	struct GLRet
	{
		T	_value;

		GLRet(): _value(0) {}
		GLRet(const T& v): _value(v) {}

		operator T const & () const	{ return _value; }
	};

	template <>
	struct GLRet<void>
	{
		GLRet(void) {}

		operator void () const	{ return void(); }
	};

	
extern "C" 
{
	
// GL interceptors //
#	undef  GL_BUILDFUNC
#	define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
		GLAPI _ret CALLBACK _func (_in_params) \
		{\
			UXTypes::string	& str = s_sGlobalString; \
			\
			str.ClearMem(); \
			str << #_func << "( "; \
			ToLog( _out_params ); \
			str << " )"; \
			\
			_hook( _out_params );\
			\
			if ( _func == nullptr ) { \
				str << "\nError: Function not supported (zero pointer)"; \
				LOG( str.cstr(), e_log_msg::ERROR_NL_0 ); \
				return (_ret)GLRet<_ret>(); \
			} \
			_switch( \
				const GLRet<_ret>	retValue; \
				GL_CALL( _func()( _out_params ) ); \
				,\
				const GLRet<_ret>	retValue( GL_CALL( _func()( _out_params ) ) ); \
				str << "  return: "; \
				str_add( str, (_ret)retValue ); \
			) \
			\
			e_log_msg::type		e_log_type = CheckGLError() ? \
											 e_log_msg::INFO_EXT : \
											 e_log_msg::WARNING_NL_0; \
			LOG( str.cstr(), e_log_type ); \
			\
			return (_ret)retValue; \
		}
	
	GL_FUNCTIONS
	GLEXT_FUNCTIONS
	

// WGL interceptors //
#	undef  GL_BUILDFUNC
#	define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
		GLAPI _ret CALLBACK _func (_in_params) \
		{\
			UXTypes::string	& str = s_sGlobalString; \
			\
			str.ClearMem(); \
			str << #_func << "( "; \
			ToLog( _out_params ); \
			str << " )"; \
			\
			_hook( _out_params );\
			\
			if ( _func == nullptr ) { \
				str << "\nError: Function not supported (zero pointer)"; \
				LOG( str.cstr(), e_log_msg::ERROR_NL_0 ); \
				return (_ret)GLRet<_ret>(); \
			} \
			_switch( \
				const GLRet<_ret>	retValue; \
				GL_CALL( _func()( _out_params ) ); \
				,\
				const GLRet<_ret>	retValue( GL_CALL( _func()( _out_params ) ) ); \
				str << "  return: "; \
				str_add( str, (_ret)retValue ); \
			) \
			\
			LOG( str.cstr(), e_log_msg::INFO_EXT ); \
			\
			return (_ret)retValue; \
		}

	WGL_FUNCTIONS
	WGLEXT_FUNCTIONS

}

	
#ifdef PLATFORM_WINDOWS
#	define GL_GETPROCADDRESS( _address )	wglGetProcAddress()( (LPCSTR)_address )
#endif

#ifdef PLATFORM_LINUX
#	define GL_GETPROCADDRESS( _address )	glXGetProcAddress()( (const GLubyte *)_address )
#endif

#ifndef GL_GETPROCADDRESS
#	error not defined "*GetProcAddress" function!
#endif

	
/*
=================================================
	конструктор
=================================================
*/
	GLSrcFunctions::GLSrcFunctions():
		_pFS(new CFileSystem()), _pLog(nullptr),
		_pInstance(nullptr), _iNumContexts(0)
	{
		// Load Libraries //
		string	s_path;
		CHECK_RETURN_R( TPlatform::GetSystemPath( s_path ), );
		s_path << "\\opengl32.dll";

		CHECK_RETURN_R( this->_sOpenGL.Load( s_path.cstr() ), );
		CHECK_RETURN_R( this->_sInteceptorLib.LoadSelf(), );


		// Init Version 1.1 //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			this->_func = (PFN##_func##PROC) this->_sOpenGL.GetProc( #_func );

		GL_FUNCTIONS
		WGL_FUNCTIONS
	}

/*
=================================================
	деструктор
=================================================
*/
	GLSrcFunctions::~GLSrcFunctions()
	{
		delete _pFS;
	}

/*
=================================================
	инициализация
=================================================
*/
	void GLSrcFunctions::Create(void *pInstance)
	{
		if ( Instance()->_pInstance == nullptr && pInstance != nullptr )
		{
			Instance()->_pInstance = pInstance;
			CHECK_RETURN_R( Instance()->_sInteceptorLib.Load( (winapi::HMODULE)pInstance ), );
		}

		if ( LOGGER() == nullptr )
		{
			CLogger *	p_logger = new CLogger();
			p_logger->Create( e_log_output::LOG_FILE_BIT |
							  e_log_output::MESSAGE_BIT, "GLLogger.html" );
			LOG( "GL Logger initialized.", e_log_msg::DEBUG );
			Instance()->_pLog = p_logger;

			if ( Instance()->_sOpenGL.IsValid() )
				LOG( (string("Loaded OpenGL library from: \"") << Instance()->_sOpenGL.GetName() << "\".").cstr(), e_log_msg::DEBUG );

			if ( Instance()->_sInteceptorLib.IsValid() )
				LOG( (string("Loaded OpenGL interceptor from: \"") << Instance()->_sInteceptorLib.GetName() << "\".").cstr(), e_log_msg::DEBUG );
		}
	}
	
/*
=================================================
	деинициализация
=================================================
*/
	void GLSrcFunctions::Release()
	{
		if ( LOGGER() != nullptr )
		{
			LOG( "GL Logger finalized.", e_log_msg::DEBUG );
			delete (CLogger *)Instance()->_pLog;
			Instance()->_pLog = nullptr;
		}
	}


#ifdef PLATFORM_WINDOWS
/*
=================================================
	создать инстанцию и
	инициализировать функции
=================================================
*/
	HGLRC GLSrcFunctions::CreateInstance(HDC hDC)
	{
		Instance()->Create();


		// Create Context //
		HGLRC	rc = nullptr;

		CHECK_RETURN( (rc = Instance()->wglCreateContext()( hDC )) != nullptr );
		CHECK_RETURN( Instance()->wglMakeCurrent()( hDC, rc ) == winapi::TRUE );
		Instance()->_iNumContexts++;

		
		// Reinit Extensions //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			Instance()->_func = (PFN##_func##PROC) Instance()->GL_GETPROCADDRESS( #_func );

		GLEXT_FUNCTIONS
		WGLEXT_FUNCTIONS


		// write context info //
		GLint	i_major = 0;
		GLint	i_minor = 0;
		Instance()->glGetIntegerv()( GL_MAJOR_VERSION, &i_major );
		Instance()->glGetIntegerv()( GL_MINOR_VERSION, &i_minor );

		string	ctx_str("Created context:\n");
		ctx_str << "\tversion: " << i_major << '.' << i_minor;
		LOG( ctx_str.cstr(), e_log_msg::INFO_0 );


		// write current pixel format info //
		int	pf = Instance()->wglGetPixelFormat()( hDC );
		PIXELFORMATDESCRIPTOR	pfd = {0};
		Instance()->wglDescribePixelFormat()( hDC, pf, sizeof(pfd), &pfd );

		string	str("Current pixel format:\n");
		str << "\tcolor bits:    " << pfd.cColorBits << "\n"
			<< "\tdepth bits:    " << pfd.cDepthBits << "\n"
			<< "\tstencil bits:  " << pfd.cStencilBits << "\n"
			<< "\tdouble buffer: " << ( (pfd.dwFlags & PFD_DOUBLEBUFFER) ? "true" : "false" );

		LOG( str.cstr(), e_log_msg::INFO_0 );

		
		Instance()->wglMakeCurrent()( hDC, nullptr );
		return rc;
	}
	
/*
=================================================
	создать инстанцию и
	инициализировать функции
=================================================
*/
	HGLRC GLSrcFunctions::CreateInstanceExt(HDC hDC, HGLRC hSharedRC, const int *attribs)
	{
		Instance()->Create();


		// Create Context //
		HGLRC	rc = nullptr;

		CHECK_RETURN( (rc = Instance()->wglCreateContextAttribsARB()( hDC, hSharedRC, attribs )) != nullptr );
		CHECK_RETURN( Instance()->wglMakeCurrent()( hDC, rc ) == winapi::TRUE );
		Instance()->_iNumContexts++;

		
		// Reinit Extensions //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			Instance()->_func = (PFN##_func##PROC) Instance()->GL_GETPROCADDRESS( #_func );

		GLEXT_FUNCTIONS
		WGLEXT_FUNCTIONS


		// write context info //
		string	ctx_str("Created extended context:\n");
		int		i_major_ver	= 0,
				i_minor_ver	= 0,
				i_flags		= 0,
				i_profile	= 0;
		
		for (usize i = 0; attribs[i] != 0; i += 2)
		{
			switch ( attribs[i] )
			{
				case WGL_CONTEXT_MAJOR_VERSION_ARB :	i_major_ver = attribs[i+1];	break;
				case WGL_CONTEXT_MINOR_VERSION_ARB :	i_minor_ver = attribs[i+1];	break;
				case WGL_CONTEXT_FLAGS_ARB :			i_flags = attribs[i+1];		break;
				case WGL_CONTEXT_PROFILE_MASK_ARB :		i_profile = attribs[i+1];	break;
			};
		}

		ctx_str << "\tversion: " << i_major_ver << '.' << i_minor_ver << '\n'
				<< "\tprofile: " << (i_profile == WGL_CONTEXT_CORE_PROFILE_BIT_ARB ? "core\n" :
									(i_profile == WGL_CONTEXT_ES2_PROFILE_BIT_EXT ? "es2\n" : "compatibility\n" ))
				<< "\tflags :  ";

		if ( i_flags == WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB )
			ctx_str << "forward_compatible, ";

		if ( i_flags == WGL_CONTEXT_DEBUG_BIT_ARB )
			ctx_str << "debug, ";

		if ( *(ctx_str.End()-1) == ',' ) { ctx_str.PopBack();  ctx_str.PopBack(); }

		LOG( ctx_str.cstr(), e_log_msg::INFO_0 );



		// write current pixel format info //
		int	pf = Instance()->wglGetPixelFormat()( hDC );
		
		const int	a_attributes[] = {	WGL_ACCELERATION_ARB, WGL_COLOR_BITS_ARB, WGL_ALPHA_BITS_ARB,
										WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB, WGL_DOUBLE_BUFFER_ARB,
										WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB, WGL_STEREO_ARB,
										WGL_COLOR_SAMPLES_NV, WGL_COVERAGE_SAMPLES_NV };
		int		a_attrib_values[ COUNT_OF( a_attributes ) ] = {0};
		
		Instance()->wglGetPixelFormatAttribivARB()( hDC, pf, PFD_MAIN_PLANE, count_of(a_attributes),
													a_attributes, a_attrib_values );

		string	str("Current pixel format (extended):\n");
		str << "\tcolor bits:       " << a_attrib_values[1] << ", alpha bits: " << a_attrib_values[2] << '\n'
			<< "\tdepth bits:       " << a_attrib_values[3] << '\n'
			<< "\tstencil bits:     " << a_attrib_values[4] << '\n'
			<< "\tmultisampling:    " << ( !!a_attrib_values[6] ? "true\n" : "false\n" )
			<< "\tsamples:          " << a_attrib_values[7] << '\n'
			<< "\tcoverage samples: " << a_attrib_values[10] << '\n'
			<< "\tcolor samples:    " << a_attrib_values[9] << '\n'
			<< "\tdouble buffer:    " << ( !!a_attrib_values[5] ? "true\n" : "false\n" )
			<< "\tstereo buffer:    " << ( !!a_attrib_values[8] ? "true\n" : "false\n" )
			<< "\tacceleration:     " << ( (a_attrib_values[0] == WGL_FULL_ACCELERATION_ARB ? "full" :
											(a_attrib_values[0] == WGL_GENERIC_ACCELERATION_ARB ? "generic" : "no") ) );
		
		LOG( str.cstr(), e_log_msg::INFO_0 );

		
		Instance()->wglMakeCurrent()( hDC, nullptr );
		return rc;
	}

#else	// not PLATFORM_WINDOWS

/*
=================================================
	создать инстанцию и
	инициализировать функции
=================================================
*/
	GLXContext GLSrcFunctions::CreateInstance(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct)
	{
		Instance()->Create();


		// Create Context //
		GLXContext	ctx = nullptr;
		GLXDrawable drawable = Instance()->glXGetCurrentDrawable()();

		CHECK_RETURN( (ctx = Instance()->glXCreateContext()( dpy, vis, shareList, direct )) != nullptr );
		CHECK_RETURN( Instance()->glXMakeCurrent()( dpy, drawable, ctx ) );
		Instance()->_iNumContexts++;

		
		// Reinit Extensions //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			Instance()->_func = (PFN##_func##PROC) Instance()->GL_GETPROCADDRESS( #_func );

		GLEXT_FUNCTIONS
		WGLEXT_FUNCTIONS


		// write context info //
		GLint	i_major = 0;
		GLint	i_minor = 0;
		Instance()->glGetIntegerv()( GL_MAJOR_VERSION, &i_major );
		Instance()->glGetIntegerv()( GL_MINOR_VERSION, &i_minor );
		
		string	ctx_str("Created context ");
		ctx_str << "0x" << string().FormatI( (usize)ctx, 16 );
		ctx_str << ", version: " << i_major << '.' << i_minor;
		LOG( ctx_str.cstr(), e_log_msg::INFO_0 );

		
		// write current pixel format info //

		
		Instance()->glXMakeCurrent()( dpy, drawable, nullptr );
		return ctx;
	}
	
/*
=================================================
	создать инстанцию и
	инициализировать функции
=================================================
*/
	GLXContext GLSrcFunctions::CreateInstanceExt(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list)
	{
		Instance()->Create();


		// Create Context //
		GLXContext	ctx = nullptr;
		GLXDrawable drawable = Instance()->glXGetCurrentDrawable()();

		CHECK_RETURN( (ctx = Instance()->glXCreateContextAttribsARB()( dpy, config, share_context, direct, attrib_list )) != nullptr );
		CHECK_RETURN( Instance()->glXMakeCurrent()( dpy, drawable, ctx ) );
		Instance()->_iNumContexts++;

		
		// Reinit Extensions //
#		undef  GL_BUILDFUNC
#		define GL_BUILDFUNC( _ret, _func, _in_params, _out_params, _hook, _switch ) \
			Instance()->_func = (PFN##_func##PROC) Instance()->GL_GETPROCADDRESS( #_func );

		GLEXT_FUNCTIONS
		WGLEXT_FUNCTIONS


		// write context info //
		GLint	i_major = 0;
		GLint	i_minor = 0;
		Instance()->glGetIntegerv()( GL_MAJOR_VERSION, &i_major );
		Instance()->glGetIntegerv()( GL_MINOR_VERSION, &i_minor );

		string	ctx_str("Created context ");
		ctx_str << "0x" << string().FormatI( (usize)ctx, 16 );
		ctx_str << ", version: " << i_major << '.' << i_minor;
		LOG( ctx_str.cstr(), e_log_msg::INFO_0 );

		
		// write current pixel format info //


		Instance()->glXMakeCurrent()( dpy, drawable, nullptr );
		return ctx;
	}

#endif	// PLATFORM_WINDOWS
	
/*
=================================================
	удалить инстанцию
=================================================
*/
	void GLSrcFunctions::ReleaseInstance(void *pRC)
	{
		string	str("Deleted context 0x");
		str << string().FormatI( (usize)pRC, 16 );
		LOG( str.cstr(), e_log_msg::INFO_0 );

		if ( !(Instance()->_iNumContexts--) )
			Release();
	}


#	undef  GL_BUILDFUNC
#	undef  CONST
	
	
extern "C"
{
	
#ifdef PLATFORM_WINDOWS
/*
=================================================
	
=================================================
*/
	GLAPI HGLRC CALLBACK wglCreateContextHook(HDC dc)
	{
		HGLRC	retValue = GLSrcFunctions::CreateInstance( dc );
		
		string	& str = s_sGlobalString;
		str << "  return: ";
		str_add( str, retValue );
		LOG( str.cstr(), e_log_msg::INFO_EXT );

		return retValue;
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI HGLRC CALLBACK wglCreateContextAttribsARBHook (HDC hDC, HGLRC hShareContext, const int *attribList)
	{
		HGLRC	retValue = GLSrcFunctions::CreateInstanceExt( hDC, hShareContext, attribList );
		
		string	& str = s_sGlobalString;
		str << "  return: ";
		str_add( str, retValue );
		LOG( str.cstr(), e_log_msg::INFO_EXT );

		return retValue;
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI BOOL  CALLBACK wglDeleteContextHook(HGLRC rc)
	{
		BOOL	retValue = GL_CALL( wglDeleteContext()( rc ) );
		GLSrcFunctions::ReleaseInstance( rc );
		
		string	& str = s_sGlobalString;
		str << "  return: ";
		str_add( str, retValue );
		LOG( str.cstr(), e_log_msg::INFO_EXT );

		return retValue;
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI PROC  CALLBACK wglGetProcAddressHook(LPCSTR lpszProc)
	{
		PROC	srcproc	= GL_CALL( GL_GETPROCADDRESS( lpszProc ) );
		PROC	proc	= srcproc == nullptr ? nullptr :
						  (PROC) GL_CALL( _sInteceptorLib.GetProc( lpszProc ) );

		string	& str = s_sGlobalString;
		str << "  return: ";
		str_add( str, proc );
		LOG( str.cstr(), e_log_msg::INFO_EXT );

		return proc;
	}

#else	// not PLATFORM_WINDOWS

/*
=================================================
	
=================================================
*/
	extern GLXContext glXCreateContextHook ( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct )
	{
		GLXContext	retValue = GLSrcFunctions::CreateInstance( dpy, vis, shareList, direct );
		
		string	& str = s_sGlobalString;
		str << "  return: ";
		str_add( str, retValue );
		LOG( str.cstr(), e_log_msg::INFO_EXT );

		return retValue;
	}
	
/*
=================================================
	
=================================================
*/
	extern GLXContext glXCreateContextAttribsARBHook (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list)
	{
		GLXContext	retValue = GLSrcFunctions::CreateInstanceExt( dpy, config, share_context, direct, attrib_list );
		
		string	& str = s_sGlobalString;
		str << "  return: ";
		str_add( str, retValue );
		LOG( str.cstr(), e_log_msg::INFO_EXT );

		return retValue;
	}
	
/*
=================================================
	
=================================================
*/
	extern void glXDestroyContextHook ( Display *dpy, GLXContext ctx )
	{
		GL_CALL( wglDeleteContext()( rc ) );
		GLSrcFunctions::ReleaseInstance( rc );
	}
	
/*
=================================================
	
=================================================
*/
	extern __GLXextFuncPtr glXGetProcAddressARBHook (const GLubyte *procName)
	{
		__GLXextFuncPtr	srcproc	= GL_CALL( GL_GETPROCADDRESS( procName ) );
		__GLXextFuncPtr	proc	= srcproc == nullptr ? nullptr :
								 (__GLXextFuncPtr) GL_CALL( _sInteceptorLib.GetProc( (const char *)procName ) );

		string	& str = s_sGlobalString;
		str << "  return: ";
		str_add( str, proc );
		LOG( str.cstr(), e_log_msg::INFO_EXT );

		return proc;
	}

#endif	// PLATFORM_WINDOWS

	
/*
=================================================
	
=================================================
*/
	GLAPI void  CALLBACK glProgramStringARBHook (GLenum target, GLenum format, GLsizei len, const GLvoid *str)
	{
		string &	source = s_sGlobalString;

		if ( target == GL_FRAGMENT_PROGRAM_ARB )
			source << "\n\tFragment program source:\n";
		else
		if ( target == GL_VERTEX_PROGRAM_ARB )
			source << "\n\tVertex program source:\n";
			
		source << uni_c_string( (const char *)str, len ) << '\n';
	}
	
/*
=================================================
	
=================================================
*/
	GLAPI void  CALLBACK glShaderSourceHook (GLuint shader, GLsizei count, const GLchar* const *str, const GLint *length)
	{
		GLint	type = 0;
		GL_CALL( glGetShaderiv()( shader, GL_SHADER_TYPE, &type ) );

		string &	source = s_sGlobalString;

		switch ( type )
		{
			case GL_FRAGMENT_SHADER	:			source << "\n\tFragment ";			break;
			case GL_VERTEX_SHADER :				source << "\n\tVertex ";			break;
			case GL_GEOMETRY_SHADER :			source << "\n\tGeometry ";			break;
			case GL_TESS_CONTROL_SHADER :		source << "\n\tTess Control ";		break;
			case GL_TESS_EVALUATION_SHADER :	source << "\n\tTess Evaluation ";	break;
			case GL_COMPUTE_SHADER :			source << "\n\tCompute ";			break;
			default :							source << "\n\tUnknown ";
		};

		source << "shader source:\n";

		for (GLsizei i = 0; i < count; ++i)
		{
			source << string( str[i], length != nullptr ? length[i] : 0 );

			if ( i < count-1 ) {
				source << "\n-----------------------------------------------------------\n\n";
			}
		}
		source << '\n';
	}


}	// extern "C"


//-------------------------------------------------------------------

}	// gl

#include <logger/ux_logger_lib.h>
